#!/usr/bin/env python
"""YChaRes

Author: Thomas Habets <habets@google.com>

YubiKey Challenge-Response

http://code.google.com/p/ychares/
http://github.com/ThomasHabets/ychares/


Dependencies:
  * Yubico Python library. http://github.com/yubico/python-yubico
  * python-usb:  apt-get install python-usb


Program mode:
  $ ychares --program
  Key: 880f9c31095f476f00a0a53f7f779a8180155fb7

Challenge-response mode:
  $ ychares --challenge
  1234                                         <-- challenge. Written by user
  7adc93b8ea0b8e3da49ddf35b0486cae0d632713     <-- reply
  123456                                       <-- challenge. Written by user
  a44ff7adfddceb890fc1e12c321cd0d2e26a2a16     <-- reply
  ^D

  $ echo 1234 | ychares --challenge
  7adc93b8ea0b8e3da49ddf35b0486cae0d632713
"""
# ------------------------------
# Copyright 2011 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ------------------------------
import sys
import yubico
import usb

SLOT=2
VERSION="0.01"

def program(yubikey, slot, key):
    cfg = yubikey.init_config()
    if len(key) != 20:
        raise "hell"
    skey = 'h:' + key.encode('hex')
    cfg.mode_challenge_response(skey, type='HMAC', variable=True)
    cfg.extended_flag('SERIAL_API_VISIBLE', True)
    yubikey.write_config(cfg, slot=slot)


def get_random_bytes(size):
    return open('/dev/urandom').read(size)


def challenge_response(yubikey, slot, challenge):
    return yubikey.challenge_response(challenge.encode('hex'), slot=slot)


def cmd_challenge():
    try:
        yubikey = yubico.find_yubikey()
    except (usb.USBError, yubico.yubikey.YubiKeyError) as inst:
        print >>sys.stderr, "Yubikey comm error: %s" % inst
        sys.exit(2)

    try:
        while True:
            challenge = raw_input('').decode('hex')
            reply = challenge_response(yubikey,
                                       SLOT,
                                       challenge)
            print reply.encode('hex')
    except EOFError:
        pass


def cmd_program():
    key = get_random_bytes(20)
    program(yubico.find_yubikey(),
            SLOT,
            key)
    print "Key: %s" % key.encode('hex')


def cmd_help():
    print """ychares %(version)s

ychares  --challenge | --program

See source for more details.""" % {'version': VERSION}


def main():
    if sys.argv[1:] == ['--program']:
        cmd_program()
    elif sys.argv[1:] == ['--challenge']:
        cmd_challenge()
    else:
        cmd_help()


if __name__ == '__main__':
    main()
